在 Day 26 中,我們解析了透視平截頭體、三維物理視差與 Z-Buffer 空間錨定,成功讓 2D 像素圖片在三維空間中建立了正確的幾何深度與遮擋關係。
然而,當幾何架構搭建完成後,許多初入 WebGL 領域的開發者會立刻撞上第二堵視覺高牆:「為什麼畫面看起來依然生硬死板,像是一堆廉價的灰色 3D 模型上,貼了幾張浮空的彩色紙片?」
這正是「幾何空間」與「真實視覺」之間的落差所在。在現實世界中,人類並不是靠幾何座標來感受深度的,而是靠光子在物體表面的反彈、衰減、漸變與陰影來辨識物體的厚度與材質。
今天我們將聚焦於本專案的另一大靈魂支柱——即時光學系統(Real-Time Lighting Architecture),深入探討 Three.js 如何透過冷暖三層光源模型、接觸陰影物理錨定,以及著色器光學乘積,把平面的 2D 貼紙雕刻出厚重扎實的三維體積!
在 Web 3D 場景中,最常見的錯誤就是「隨便放一顆全域白光,把整個場景均勻照亮」。這種照明方式會抹平所有物體的明暗交界線,直接扼殺空間感。
在專案中,我們設計了一套「冷暖互補的三層光學模型」,從微觀到宏觀層層鎖定光學秩序:
如果場景中沒有環境光,所有照不到光線的背光面都會直接掉進 rgb(0, 0, 0) 的純黑深淵中。這在真實物理中是不存在的,因為大氣與地面永遠存在漫反射光。
單有環境光,場景只是一片灰藍。我們在天頂架設了一道高角度斜射的平行光(DirectionalLight,模擬穿透地宮殘垣的冷白月光):
法線明暗對比(Normal Shading):光線從斜上方 45 度射下時,水平向上的「地面石磚」接收到最高光通量而變亮;而垂直向下的「牆壁側立面」因夾角變大而變暗。
長條幾何投影:石牆與殘壁在地面上投射出清晰的長條影子,精準交代了「這堵牆到底有多高」、「這根柱子立在哪裡」。
前兩層光全是「冷色調」,營造出古堡遺跡幽深、潮濕且荒涼的基調。
而主角手中的隨身火把(PointLight),則採用了 1850K 黑體輻射的暖金琥珀色:
在 3D 遊戲開發中,有一個極其頑固的視覺痛點:紙片人角色看起來永遠像「浮在半空中」。
人類大腦判斷一個物體「是否真正接觸地面」的唯一視覺依據,就是觀察物體底部與地面的接觸陰影(Ambient Occlusion / Contact Shadow)。
3D 模型有厚度,模型底部的多邊形與地面重疊時能產生自然的自遮擋。
但 2D 精靈圖是一張垂直的紙片,它與地面的接觸面在幾何上只有「一條厚度為 0 的線」!如果只靠全域陰影貼圖,微弱的偏差就會讓角色與影子脫節,產生詭異的浮空感。
為了給紙片人注入實體重量感,我們在角色的腳底正下方(y = 1.02,剛好略高於地面石磚 y = 1.0 以防止 Z-Fighting 深度衝突),放置了一塊專屬的圓形接觸陰影面片:
// 角色貼地接觸陰影 (物理膠水)
const shadowGeo = new THREE.PlaneGeometry(1.2, 0.6);
const shadowMat = new THREE.MeshBasicMaterial({
map: createSoftShadowTexture(), // 核心由黑向外漸變的高斯軟邊貼圖
transparent: true,
opacity: 0.6,
depthWrite: false // 禁止寫入深度緩衝,避免阻礙角色本身的遮擋渲染
});
const footShadow = new THREE.Mesh(shadowGeo, shadowMat);
footShadow.rotation.x = -Math.PI / 2; // 水平躺平成為地面陰影
playerGroup.add(footShadow);
橢圓形扁平幾何:考慮到 28 度的俯視透視相機角度,正圓形的陰影在螢幕上會變形。我們採用長寬比 $1.2 : 0.6$ 的扁橢圓形,在相機投影後剛好呈現自然的受壓散焦感。
高斯核心衰減:陰影貼圖中心為純黑高濃度、向外平滑漸變至透明。
視覺心理學效應:當角色的足跡與這片緊貼地面的軟陰影完全重合時,大腦會瞬間產生「這是一個具備數十公斤重量的實體角色踏在石板上」的強烈錯覺,浮空感徹底消散!
三、著色器光學乘積:把 2D 貼紙雕刻成立體圓柱體
如果 2D 紙片人始終保持貼圖原色,那麼無論周圍的光影多麼華麗,角色依然像一張浮在動態背景上的「發光色塊」。
Three.js 原生的 SpriteMaterial 是不參與場景光照計算的(Unlit)。為了解決這個斷層,我們在 Day 20 透過 onBeforeCompile 在片段著色器中實現了「動態受光乘積」:
這行乘積背後蘊藏著極為精妙的圖形學構想:
一張 2D 面片在數學上是平的,它的表面法線永遠筆直朝向攝影機。如果直接拿面片法線去算光照,整張圖會呈現死板的「整張變亮」或「整張變暗」。我們在著色器中利用精靈圖的橫向局部座標($vLocalX \in [0.0, 1.0]$),在像素著色階段人工虛擬出了一個微曲面的圓弧法線:
靠近火把的那一側(迎光面):賦予高光通量與暖金反射色(warmLight)。
背向火把的那一側(背光面):平滑衰減為周圍環境的清冷月藍色(coolShadow)。
中間過渡(smoothstep):模擬人體身軀圓柱體的幾何弧度,形成細膩的明暗交界線。
在著色器底層,當我們把貼圖原色乘上這個動態光學係數時:
主角衣服的像素原本是綠色,在迎光側被染上一層薄薄的暖金晨曦感;
在背光側,綠色則自然沉降為深暗且帶有些許藍調的冷暗綠;
當火把隨著三諧波演算法產生呼吸律動時,角色身上的暖光也同步起伏。
2D 紙片人在這一刻不再是一張獨立的圖片,而是與整個 3D 空間的光子進行著即時的光學能量交換。
即時光影不僅負責「照亮」,在 HD-2D 的地宮氛圍中,光影更重要的任務是「定義未知的邊界」。
在專案中,我們嚴格校準了火把的照射半徑(distance = 18)與衰減指數(decay = 1.85):
中心高照度區(半徑 0 ~ 4 單位):角色的踏步、手中的木杖與腳下的石磚細節分明,帶給玩家強烈的安全感。
過渡半影區(半徑 4 ~ 12 單位):光線能量呈非線性快速崩落,遠處的石柱在光影交界處若隱若現,牆面裂痕拉出長長的傾斜黑影。
未知深邃區(半徑 12 單位以上):光線完全消逝在深藍色的環境霧氣中,將地宮深處的幽暗留給玩家的想像力。
這種明確的光線作用範圍,讓 3D 空間擁有了「近處清晰聚焦、遠處神秘隱沒」的呼吸節奏,為後續的後處理管線打下了完美的明暗基礎。
今天我們揭開了賦予平面 2D 資源「三維體積」的光學密碼:
三層光學模型:以環境底色防死黑、高角度月光刻立面、暖金火把鎖焦點,構建具備冷暖對比的空間秩序。
接觸陰影膠水:以貼地扁橢圓高斯軟邊面片,填補紙片人無體積的物理缺陷,徹底消除漂浮感。
著色器曲面受光:在 GLSL 片段階段將 2D 平面虛擬為 3D 圓柱體,藉由動態光影乘積讓紙片人融入場景。
非線性衰減界限:透過光線在空間中的崩落,勾勒出地宮由明入暗的幽閉縱深。
【本日結語】
現在,幾何透視有了,立體光學也有了。但如果直接將畫面輸出到螢幕上,它依然帶有一種現代 3D 軟體的「數位生硬感」。如何將這份數位畫面打碎,轉化為帶有微縮模型玩具感與膠卷質感的電影級畫面?
在下一篇 Day 28 中,我們將剖析最後的視覺魔法——打碎數位生硬感!後處理管線(Post-Processing)與鏡頭光學模擬!我們將深入推導自研的 TiltShiftVignetteShader 移軸景深、高斯卷積散景,以及 UnrealBloomPass 的極限高門檻漫射光暈!